"""
资产配置

提供资产配置策略、优化算法、风险预算等功能。
"""

from typing import Dict, List, Optional, Any, Callable, Tuple
from datetime import datetime, timedelta
from dataclasses import dataclass, field
from enum import Enum
import asyncio
from decimal import Decimal
import math


class AllocationMethod(Enum):
    """配置方法"""
    EQUAL_WEIGHT = "equal_weight"  # 等权重
    MARKET_CAP = "market_cap"  # 市值加权
    RISK_PARITY = "risk_parity"  # 风险平价
    MEAN_VARIANCE = "mean_variance"  # 均值方差优化
    BLACK_LITTERMAN = "black_litterman"  # Black-Litterman模型
    MINIMUM_VARIANCE = "minimum_variance"  # 最小方差
    MAXIMUM_SHARPE = "maximum_sharpe"  # 最大夏普比率
    CUSTOM = "custom"  # 自定义


class RebalanceFrequency(Enum):
    """再平衡频率"""
    DAILY = "daily"  # 每日
    WEEKLY = "weekly"  # 每周
    MONTHLY = "monthly"  # 每月
    QUARTERLY = "quarterly"  # 每季度
    ANNUALLY = "annually"  # 每年
    THRESHOLD = "threshold"  # 阈值触发


@dataclass
class AssetInfo:
    """资产信息"""
    symbol: str
    name: str
    asset_class: str  # 资产类别
    sector: str = ""  # 行业
    market_cap: Decimal = Decimal('0')  # 市值
    expected_return: float = 0.0  # 预期收益率
    volatility: float = 0.0  # 波动率
    beta: float = 1.0  # Beta系数
    correlation_matrix: Dict[str, float] = field(default_factory=dict)  # 相关性矩阵


@dataclass
class AllocationConstraint:
    """配置约束"""
    min_weight: float = 0.0  # 最小权重
    max_weight: float = 1.0  # 最大权重
    target_weight: Optional[float] = None  # 目标权重
    weight_tolerance: float = 0.05  # 权重容忍度
    sector_limit: Optional[float] = None  # 行业限制
    asset_class_limit: Optional[float] = None  # 资产类别限制


@dataclass
class AllocationResult:
    """配置结果"""
    weights: Dict[str, float]  # 权重分配
    expected_return: float  # 预期收益率
    expected_risk: float  # 预期风险
    sharpe_ratio: float  # 夏普比率
    allocation_method: AllocationMethod  # 配置方法
    constraints_satisfied: bool  # 约束是否满足
    optimization_details: Dict[str, Any] = field(default_factory=dict)  # 优化详情
    created_time: datetime = field(default_factory=datetime.now)


class AllocationStrategy:
    """
    配置策略基类
    
    定义资产配置的通用接口。
    """
    
    def __init__(self, name: str, method: AllocationMethod):
        """初始化配置策略"""
        self.name = name
        self.method = method
        self.constraints: Dict[str, AllocationConstraint] = {}
        self.risk_free_rate = 0.03  # 无风险利率
    
    def add_constraint(self, symbol: str, constraint: AllocationConstraint) -> None:
        """添加约束条件"""
        self.constraints[symbol] = constraint
    
    def calculate_weights(
        self,
        assets: Dict[str, AssetInfo],
        price_data: Optional[Dict[str, List[Decimal]]] = None,
        **kwargs
    ) -> AllocationResult:
        """计算权重分配"""
        raise NotImplementedError("子类必须实现此方法")
    
    def validate_constraints(self, weights: Dict[str, float], assets: Dict[str, AssetInfo]) -> bool:
        """验证约束条件"""
        for symbol, weight in weights.items():
            if symbol in self.constraints:
                constraint = self.constraints[symbol]
                
                # 检查权重范围
                if weight < constraint.min_weight or weight > constraint.max_weight:
                    return False
                
                # 检查目标权重容忍度
                if constraint.target_weight is not None:
                    if abs(weight - constraint.target_weight) > constraint.weight_tolerance:
                        return False
        
        # 检查行业和资产类别限制
        sector_weights = {}
        asset_class_weights = {}
        
        for symbol, weight in weights.items():
            if symbol in assets:
                asset = assets[symbol]
                
                # 累计行业权重
                if asset.sector:
                    if asset.sector not in sector_weights:
                        sector_weights[asset.sector] = 0.0
                    sector_weights[asset.sector] += weight
                
                # 累计资产类别权重
                if asset.asset_class not in asset_class_weights:
                    asset_class_weights[asset.asset_class] = 0.0
                asset_class_weights[asset.asset_class] += weight
        
        # 检查行业限制
        for symbol in weights:
            if symbol in self.constraints and symbol in assets:
                constraint = self.constraints[symbol]
                asset = assets[symbol]
                
                if constraint.sector_limit and asset.sector:
                    if sector_weights.get(asset.sector, 0) > constraint.sector_limit:
                        return False
                
                if constraint.asset_class_limit:
                    if asset_class_weights.get(asset.asset_class, 0) > constraint.asset_class_limit:
                        return False
        
        return True


class EqualWeightStrategy(AllocationStrategy):
    """等权重配置策略"""
    
    def __init__(self):
        super().__init__("等权重配置", AllocationMethod.EQUAL_WEIGHT)
    
    def calculate_weights(
        self,
        assets: Dict[str, AssetInfo],
        price_data: Optional[Dict[str, List[Decimal]]] = None,
        **kwargs
    ) -> AllocationResult:
        """计算等权重分配"""
        n_assets = len(assets)
        if n_assets == 0:
            return AllocationResult(
                weights={},
                expected_return=0.0,
                expected_risk=0.0,
                sharpe_ratio=0.0,
                allocation_method=self.method,
                constraints_satisfied=True
            )
        
        equal_weight = 1.0 / n_assets
        weights = {symbol: equal_weight for symbol in assets.keys()}
        
        # 应用约束调整
        weights = self._apply_constraints(weights, assets)
        
        # 计算预期收益和风险
        expected_return = self._calculate_expected_return(weights, assets)
        expected_risk = self._calculate_expected_risk(weights, assets)
        sharpe_ratio = (expected_return - self.risk_free_rate) / expected_risk if expected_risk > 0 else 0.0
        
        return AllocationResult(
            weights=weights,
            expected_return=expected_return,
            expected_risk=expected_risk,
            sharpe_ratio=sharpe_ratio,
            allocation_method=self.method,
            constraints_satisfied=self.validate_constraints(weights, assets)
        )
    
    def _apply_constraints(self, weights: Dict[str, float], assets: Dict[str, AssetInfo]) -> Dict[str, float]:
        """应用约束条件调整权重"""
        adjusted_weights = weights.copy()
        
        # 应用最小最大权重约束
        for symbol in adjusted_weights:
            if symbol in self.constraints:
                constraint = self.constraints[symbol]
                adjusted_weights[symbol] = max(constraint.min_weight, 
                                             min(constraint.max_weight, adjusted_weights[symbol]))
        
        # 重新归一化
        total_weight = sum(adjusted_weights.values())
        if total_weight > 0:
            adjusted_weights = {symbol: weight / total_weight 
                              for symbol, weight in adjusted_weights.items()}
        
        return adjusted_weights
    
    def _calculate_expected_return(self, weights: Dict[str, float], assets: Dict[str, AssetInfo]) -> float:
        """计算预期收益率"""
        return sum(weights[symbol] * assets[symbol].expected_return 
                  for symbol in weights if symbol in assets)
    
    def _calculate_expected_risk(self, weights: Dict[str, float], assets: Dict[str, AssetInfo]) -> float:
        """计算预期风险"""
        # 简化的风险计算（假设相关性为0）
        variance = sum((weights[symbol] * assets[symbol].volatility) ** 2 
                      for symbol in weights if symbol in assets)
        return math.sqrt(variance)


class MarketCapStrategy(AllocationStrategy):
    """市值加权配置策略"""
    
    def __init__(self):
        super().__init__("市值加权配置", AllocationMethod.MARKET_CAP)
    
    def calculate_weights(
        self,
        assets: Dict[str, AssetInfo],
        price_data: Optional[Dict[str, List[Decimal]]] = None,
        **kwargs
    ) -> AllocationResult:
        """计算市值加权分配"""
        total_market_cap = sum(assets[symbol].market_cap for symbol in assets)
        
        if total_market_cap == 0:
            # 如果没有市值数据，回退到等权重
            return EqualWeightStrategy().calculate_weights(assets, price_data, **kwargs)
        
        weights = {
            symbol: float(assets[symbol].market_cap / total_market_cap)
            for symbol in assets
        }
        
        # 应用约束调整
        weights = self._apply_constraints(weights, assets)
        
        # 计算预期收益和风险
        expected_return = self._calculate_expected_return(weights, assets)
        expected_risk = self._calculate_expected_risk(weights, assets)
        sharpe_ratio = (expected_return - self.risk_free_rate) / expected_risk if expected_risk > 0 else 0.0
        
        return AllocationResult(
            weights=weights,
            expected_return=expected_return,
            expected_risk=expected_risk,
            sharpe_ratio=sharpe_ratio,
            allocation_method=self.method,
            constraints_satisfied=self.validate_constraints(weights, assets)
        )
    
    def _apply_constraints(self, weights: Dict[str, float], assets: Dict[str, AssetInfo]) -> Dict[str, float]:
        """应用约束条件调整权重"""
        equal_weight_strategy = EqualWeightStrategy()
        return equal_weight_strategy._apply_constraints(weights, assets)
    
    def _calculate_expected_return(self, weights: Dict[str, float], assets: Dict[str, AssetInfo]) -> float:
        """计算预期收益率"""
        equal_weight_strategy = EqualWeightStrategy()
        return equal_weight_strategy._calculate_expected_return(weights, assets)
    
    def _calculate_expected_risk(self, weights: Dict[str, float], assets: Dict[str, AssetInfo]) -> float:
        """计算预期风险"""
        equal_weight_strategy = EqualWeightStrategy()
        return equal_weight_strategy._calculate_expected_risk(weights, assets)


class RiskParityStrategy(AllocationStrategy):
    """风险平价配置策略"""
    
    def __init__(self):
        super().__init__("风险平价配置", AllocationMethod.RISK_PARITY)
    
    def calculate_weights(
        self,
        assets: Dict[str, AssetInfo],
        price_data: Optional[Dict[str, List[Decimal]]] = None,
        **kwargs
    ) -> AllocationResult:
        """计算风险平价分配"""
        # 计算风险贡献的倒数作为权重
        risk_contributions = {}
        for symbol, asset in assets.items():
            if asset.volatility > 0:
                risk_contributions[symbol] = 1.0 / asset.volatility
            else:
                risk_contributions[symbol] = 1.0
        
        total_risk_contribution = sum(risk_contributions.values())
        
        if total_risk_contribution == 0:
            return EqualWeightStrategy().calculate_weights(assets, price_data, **kwargs)
        
        weights = {
            symbol: contribution / total_risk_contribution
            for symbol, contribution in risk_contributions.items()
        }
        
        # 应用约束调整
        weights = self._apply_constraints(weights, assets)
        
        # 计算预期收益和风险
        expected_return = self._calculate_expected_return(weights, assets)
        expected_risk = self._calculate_expected_risk(weights, assets)
        sharpe_ratio = (expected_return - self.risk_free_rate) / expected_risk if expected_risk > 0 else 0.0
        
        return AllocationResult(
            weights=weights,
            expected_return=expected_return,
            expected_risk=expected_risk,
            sharpe_ratio=sharpe_ratio,
            allocation_method=self.method,
            constraints_satisfied=self.validate_constraints(weights, assets)
        )
    
    def _apply_constraints(self, weights: Dict[str, float], assets: Dict[str, AssetInfo]) -> Dict[str, float]:
        """应用约束条件调整权重"""
        equal_weight_strategy = EqualWeightStrategy()
        return equal_weight_strategy._apply_constraints(weights, assets)
    
    def _calculate_expected_return(self, weights: Dict[str, float], assets: Dict[str, AssetInfo]) -> float:
        """计算预期收益率"""
        equal_weight_strategy = EqualWeightStrategy()
        return equal_weight_strategy._calculate_expected_return(weights, assets)
    
    def _calculate_expected_risk(self, weights: Dict[str, float], assets: Dict[str, AssetInfo]) -> float:
        """计算预期风险"""
        equal_weight_strategy = EqualWeightStrategy()
        return equal_weight_strategy._calculate_expected_risk(weights, assets)


class MinimumVarianceStrategy(AllocationStrategy):
    """最小方差配置策略"""
    
    def __init__(self):
        super().__init__("最小方差配置", AllocationMethod.MINIMUM_VARIANCE)
    
    def calculate_weights(
        self,
        assets: Dict[str, AssetInfo],
        price_data: Optional[Dict[str, List[Decimal]]] = None,
        **kwargs
    ) -> AllocationResult:
        """计算最小方差分配"""
        # 简化的最小方差优化（假设相关性矩阵为单位矩阵）
        variances = {symbol: asset.volatility ** 2 for symbol, asset in assets.items()}
        
        # 权重与方差的倒数成正比
        inv_variances = {symbol: 1.0 / var if var > 0 else 1.0 
                        for symbol, var in variances.items()}
        
        total_inv_variance = sum(inv_variances.values())
        
        if total_inv_variance == 0:
            return EqualWeightStrategy().calculate_weights(assets, price_data, **kwargs)
        
        weights = {
            symbol: inv_var / total_inv_variance
            for symbol, inv_var in inv_variances.items()
        }
        
        # 应用约束调整
        weights = self._apply_constraints(weights, assets)
        
        # 计算预期收益和风险
        expected_return = self._calculate_expected_return(weights, assets)
        expected_risk = self._calculate_expected_risk(weights, assets)
        sharpe_ratio = (expected_return - self.risk_free_rate) / expected_risk if expected_risk > 0 else 0.0
        
        return AllocationResult(
            weights=weights,
            expected_return=expected_return,
            expected_risk=expected_risk,
            sharpe_ratio=sharpe_ratio,
            allocation_method=self.method,
            constraints_satisfied=self.validate_constraints(weights, assets)
        )
    
    def _apply_constraints(self, weights: Dict[str, float], assets: Dict[str, AssetInfo]) -> Dict[str, float]:
        """应用约束条件调整权重"""
        equal_weight_strategy = EqualWeightStrategy()
        return equal_weight_strategy._apply_constraints(weights, assets)
    
    def _calculate_expected_return(self, weights: Dict[str, float], assets: Dict[str, AssetInfo]) -> float:
        """计算预期收益率"""
        equal_weight_strategy = EqualWeightStrategy()
        return equal_weight_strategy._calculate_expected_return(weights, assets)
    
    def _calculate_expected_risk(self, weights: Dict[str, float], assets: Dict[str, AssetInfo]) -> float:
        """计算预期风险"""
        equal_weight_strategy = EqualWeightStrategy()
        return equal_weight_strategy._calculate_expected_risk(weights, assets)


class AssetAllocator:
    """
    资产配置器
    
    管理资产配置策略，执行配置优化。
    """
    
    def __init__(self):
        """初始化资产配置器"""
        self.strategies: Dict[str, AllocationStrategy] = {}
        self.assets: Dict[str, AssetInfo] = {}
        self.current_allocation: Optional[AllocationResult] = None
        self.allocation_history: List[AllocationResult] = []
        
        # 默认策略
        self._register_default_strategies()
        
        # 回调函数
        self.on_allocation_updated_callbacks: List[Callable] = []
    
    def _register_default_strategies(self) -> None:
        """注册默认策略"""
        self.strategies["equal_weight"] = EqualWeightStrategy()
        self.strategies["market_cap"] = MarketCapStrategy()
        self.strategies["risk_parity"] = RiskParityStrategy()
        self.strategies["minimum_variance"] = MinimumVarianceStrategy()
    
    def add_strategy(self, name: str, strategy: AllocationStrategy) -> None:
        """添加配置策略"""
        self.strategies[name] = strategy
    
    def add_asset(self, asset: AssetInfo) -> None:
        """添加资产"""
        self.assets[asset.symbol] = asset
    
    def remove_asset(self, symbol: str) -> None:
        """移除资产"""
        if symbol in self.assets:
            del self.assets[symbol]
    
    def update_asset_info(self, symbol: str, **kwargs) -> None:
        """更新资产信息"""
        if symbol in self.assets:
            asset = self.assets[symbol]
            for key, value in kwargs.items():
                if hasattr(asset, key):
                    setattr(asset, key, value)
    
    def calculate_allocation(
        self,
        strategy_name: str,
        symbols: Optional[List[str]] = None,
        **kwargs
    ) -> AllocationResult:
        """计算资产配置"""
        if strategy_name not in self.strategies:
            raise ValueError(f"策略 {strategy_name} 不存在")
        
        strategy = self.strategies[strategy_name]
        
        # 选择资产
        if symbols:
            selected_assets = {symbol: self.assets[symbol] 
                             for symbol in symbols if symbol in self.assets}
        else:
            selected_assets = self.assets.copy()
        
        if not selected_assets:
            raise ValueError("没有可用的资产进行配置")
        
        # 执行配置计算
        result = strategy.calculate_weights(selected_assets, **kwargs)
        
        # 保存结果
        self.current_allocation = result
        self.allocation_history.append(result)
        
        # 触发回调
        for callback in self.on_allocation_updated_callbacks:
            try:
                callback(result)
            except Exception as e:
                print(f"配置更新回调执行失败: {e}")
        
        return result
    
    def get_current_allocation(self) -> Optional[AllocationResult]:
        """获取当前配置"""
        return self.current_allocation
    
    def get_allocation_history(self, limit: Optional[int] = None) -> List[AllocationResult]:
        """获取配置历史"""
        if limit:
            return self.allocation_history[-limit:]
        return self.allocation_history.copy()
    
    def compare_strategies(
        self,
        strategy_names: List[str],
        symbols: Optional[List[str]] = None,
        **kwargs
    ) -> Dict[str, AllocationResult]:
        """比较不同策略"""
        results = {}
        
        for strategy_name in strategy_names:
            if strategy_name in self.strategies:
                try:
                    result = self.calculate_allocation(strategy_name, symbols, **kwargs)
                    results[strategy_name] = result
                except Exception as e:
                    print(f"策略 {strategy_name} 计算失败: {e}")
        
        return results
    
    def get_strategy_summary(self) -> Dict[str, Any]:
        """获取策略摘要"""
        return {
            'available_strategies': list(self.strategies.keys()),
            'total_assets': len(self.assets),
            'current_allocation': self.current_allocation.allocation_method.value if self.current_allocation else None,
            'allocation_history_count': len(self.allocation_history)
        }
    
    def validate_allocation(self, allocation: AllocationResult) -> Dict[str, Any]:
        """验证配置结果"""
        validation_result = {
            'is_valid': True,
            'errors': [],
            'warnings': []
        }
        
        # 检查权重总和
        total_weight = sum(allocation.weights.values())
        if abs(total_weight - 1.0) > 0.01:
            validation_result['errors'].append(f"权重总和 {total_weight:.4f} 不等于1")
            validation_result['is_valid'] = False
        
        # 检查负权重
        negative_weights = [symbol for symbol, weight in allocation.weights.items() if weight < 0]
        if negative_weights:
            validation_result['errors'].append(f"存在负权重: {negative_weights}")
            validation_result['is_valid'] = False
        
        # 检查约束满足情况
        if not allocation.constraints_satisfied:
            validation_result['warnings'].append("部分约束条件未满足")
        
        # 检查风险收益指标
        if allocation.expected_risk <= 0:
            validation_result['warnings'].append("预期风险为零或负值")
        
        if allocation.sharpe_ratio < 0:
            validation_result['warnings'].append("夏普比率为负值")
        
        return validation_result